//#include "stdafx.h"

#include "dispatch_struct.hpp"
#include <iostream>
#include <fstream>
#include <iterator>

#include <boost/program_options/options_description.hpp>
#include <boost/program_options.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>

namespace po = boost::program_options; 
using namespace std;

// A helper function to simplify the main part.
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
    copy(v.begin(), v.end(), ostream_iterator<T>(cout, " ")); 
    return os;
}


std::string const& dispatch_struct::get_URI()
{
	return _URI;
}

std::vector< std::string > const& dispatch_struct::get_input_element_name()
{
	return input_element_name;
}

std::vector< dispatch_struct::input_type > const& dispatch_struct::get_input_element_type()
{
	return input_element_type;
}

std::vector< std::string > const& dispatch_struct::get_input_element_value()
{

	if(!input_data_filename.empty()) // priority to separated file
	{
		dispatch_struct_data dispatch_data(input_data_filename.c_str());
		input_element_value = dispatch_data.get_input_element_value();
	}

	return input_element_value;
	
}

std::vector< std::string > const& dispatch_struct::get_input_element_encryption()
{
	return input_element_encryption;
}

std::vector< dispatch_struct::encryption_task > const&  dispatch_struct::get_input_encryption_task_type()
{
	return encryption_task_type;
}

std::string const& dispatch_struct::get_dispatch_struct_filename()
{
	return dispatch_struct_filename;
}

std::string const& dispatch_struct::get_next_dispatch_filename()
{
	return next_dispatch_struct_filename;
}

std::string const& dispatch_struct::get_input_data_filename()
{
	return input_data_filename;
}

std::ostream& operator << (std::ostream& ost, dispatch_struct const& c)
{
	ost << "URI = " << c._URI << "\n";
	ost << "input element names = " << c.input_element_name << "\n";
	ost << "input element types = " << c.input_element_type << "\n";
	ost << "input element values = " << static_cast<dispatch_struct>(c).get_input_element_value() << "\n";
	ost << "input element encryption configurations = " << c.input_element_encryption << "\n";
	ost << "encryption task type = " << c.encryption_task_type << std::endl;
	ost << "dispatch structure filename = " << c.dispatch_struct_filename << std::endl;	
	ost << "next dispatch structure filename to be processed = " << c.next_dispatch_struct_filename << std::endl;
	ost << "posting url encoded data = " << c.posting_url_encoded << std::endl;
	ost << "regex to extract element names = " << c.input_element_name_regex << std::endl;
	ost << "input data filename = " << c.input_data_filename << std::endl;
	return ost;
}


dispatch_struct& dispatch_struct::operator=(dispatch_struct const& _cfg)
{
	_URI = _cfg._URI;
	input_element_name = _cfg.input_element_name;
	input_element_type = _cfg.input_element_type;
	input_element_value = _cfg.input_element_value;
	input_element_encryption = _cfg.input_element_encryption;	
	encryption_task_type = _cfg.encryption_task_type;
	dispatch_struct_filename = _cfg.dispatch_struct_filename;
	next_dispatch_struct_filename = _cfg.next_dispatch_struct_filename;
	posting_url_encoded = _cfg.posting_url_encoded;
	input_element_name_regex = _cfg.input_element_name_regex;
	input_data_filename = _cfg.input_data_filename;

	return *this;
}

const char* dispatch_struct::DISPATCH_STRUCT_OP[dispatch_struct::DISPATCH_STRUCT_TOT_OP][2] = 
{
	{"URI", "URL"},
	{"input-element-names", "blank separated list of HTML input element names"},
	{"input-element-types", "blank separated list of HTML input element types"},
	{"input-element-values", "blank separated list of input element values to be assigned"},
	{"input-element-encryption", "blank separated list of encryption configurations"},
	{"encryption-task", "encrypting or decrypting"},
	{"next-dispatch", "filename of next dispatch to be processed"},
	{"post-url-encoded", "whether to post url encoded data"},
	{"input-element-name-regex", "regex to extract element names"},
	{"include-input-data-file", "filename for input data"}
};


dispatch_struct::dispatch_struct(const std::string _filename)
{
	dispatch_struct_filename = _filename;
	init();
}


dispatch_struct::dispatch_struct(const char* _filename)
{	
	dispatch_struct_filename = _filename;
	init();     

}

void dispatch_struct::init(void)
{
	try {   

		if(dispatch_struct_filename.empty())
		{
			throw ("dispatch structure filename has to be specified: -c /path_to_dispatch_struct/filename"); 
		}

        // Declare a group of options that will be 
        // allowed both on command line and in
        // config file
#ifdef _WIN32
        po::options_description config("Configuration");
#else
	po::options_description config("Configuration", po::options_description::m_default_line_length*2);
#endif
        config.add_options()	
		(DISPATCH_STRUCT_OP[dispatch_struct::URI][0], po::value< std::string >(&_URI)->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::URI][1])
		(DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_NAME][0], po::value< std::string >()->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_NAME][1])    
		(DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_TYPE][0], po::value< std::string >()->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_NAME][1])
		(DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_VALUE][0], po::value< std::string >()->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_VALUE][1])
		(DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_ENCRYPTION_CONFIG][0], po::value< std::string >()->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_ENCRYPTION_CONFIG][1])
		(DISPATCH_STRUCT_OP[dispatch_struct::ENCRYPTION_TASK][0], po::value< std::string >()->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::ENCRYPTION_TASK][1])
		(DISPATCH_STRUCT_OP[dispatch_struct::NEXT_DISPATCH_FILE][0], po::value< std::string >()->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::NEXT_DISPATCH_FILE][1])
		(DISPATCH_STRUCT_OP[dispatch_struct::POSTING_URL_ENCODED][0], po::value< bool >(&posting_url_encoded)->default_value(false), DISPATCH_STRUCT_OP[dispatch_struct::POSTING_URL_ENCODED][1])		
		(DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEMENT_NAME_REGEX][0], po::value< std::string >()->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEMENT_NAME_REGEX][1])
		(DISPATCH_STRUCT_OP[dispatch_struct::INPUT_DATA_FILENAME][0], po::value< std::string >(&input_data_filename)->default_value(""), DISPATCH_STRUCT_OP[dispatch_struct::INPUT_DATA_FILENAME][1])
		;

        po::options_description config_file_options;
        config_file_options.add(config);
      
        po::variables_map vm;

		ifstream ifs(dispatch_struct_filename.c_str());
    	store(parse_config_file(ifs, config_file_options), vm);
    	notify(vm);           	
	
	if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::URI][0]].empty())
	{
		_URI = vm[DISPATCH_STRUCT_OP[dispatch_struct::URI][0]].as< std::string >();
	}

	if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::NEXT_DISPATCH_FILE][0]].empty())
	{
		next_dispatch_struct_filename = vm[DISPATCH_STRUCT_OP[dispatch_struct::NEXT_DISPATCH_FILE][0]].as< std::string >();
	}

	if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_DATA_FILENAME][0]].empty())
	{
		input_data_filename = vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_DATA_FILENAME][0]].as< std::string >();
	}

	if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_NAME][0]].empty())
        {
                std::string input(vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_NAME][0]].as< std::string>());

				input_element_name_regex.clear();
				if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEMENT_NAME_REGEX][0]].empty())
				{
					input_element_name_regex = vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEMENT_NAME_REGEX][0]].as< std::string >();
					std::cout << "read regex = " << input_element_name_regex << std::endl;
				}

				if(input_element_name_regex.empty())
				{
					input_element_name_regex = "\\s+";
					std::cout << "default regex = " << input_element_name_regex << std::endl;
				}

				boost::regex expression(input_element_name_regex.c_str());
                       
		boost::regex_split(std::back_inserter(input_element_name), input, expression); 
/*
		for (std::vector<std::string>::iterator it = input_element_name.begin(), 
         		end = input_element_name.end(); it != end; ++it) { 
			std::cout << *it << endl; 
		} 
*/				
        }

	if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_TYPE][0]].empty())
	{
		std::string input(vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_TYPE][0]].as< std::string>());
		boost::regex expression("\\s+");
		std::vector< std::string > input_element_type_as_char;
                       
		boost::regex_split(std::back_inserter(input_element_type_as_char), input, expression); 

		for (std::vector<std::string>::iterator it = input_element_type_as_char.begin(), 
			end = input_element_type_as_char.end(); it != end; ++it) 
		{ 
			char c = (*it).at(0);
			switch(c)
			{
			case 'T': case 't': input_element_type.push_back(TEXT); break;
			case 'I': case 'i': input_element_type.push_back(IMAGE); break;
			case 'H': case 'h': input_element_type.push_back(HIDDEN); break;
			case 'P': case 'p': input_element_type.push_back(PASSWORD); break;
			case 'F': case 'f': input_element_type.push_back(FORM_ACTION); break;
			case 'B': case 'b': input_element_type.push_back(BUTTON); break;
			case 'J': case 'j': input_element_type.push_back(JSCRIPT); break;
				default: break;
			} 
		} 
	}


	if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_VALUE][0]].empty())
        {
                std::string input(vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_VALUE][0]].as< std::string>());
                boost::regex expression("\\s+");
     
                boost::regex_split(std::back_inserter(input_element_value), input, expression);
/*
                for (std::vector<std::string>::iterator it = input_element_value.begin(),
                        end = input_element_value.end(); it != end; ++it) {
			std::cout << *it << endl; 
                } 
*/
        }

		if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_ENCRYPTION_CONFIG][0]].empty())
        {
                std::string input(vm[DISPATCH_STRUCT_OP[dispatch_struct::INPUT_ELEM_ENCRYPTION_CONFIG][0]].as< std::string>());
                boost::regex expression("\\s+");
     
                boost::regex_split(std::back_inserter(input_element_encryption), input, expression);
/*
                for (std::vector<std::string>::iterator it = input_element_config.begin(),
                        end = input_element_config.end(); it != end; ++it) {
                        std::cout << *it << endl;
                }
*/
        }

		if(!vm[DISPATCH_STRUCT_OP[dispatch_struct::ENCRYPTION_TASK][0]].empty())
		{
			std::string input(vm[DISPATCH_STRUCT_OP[dispatch_struct::ENCRYPTION_TASK][0]].as< std::string>());
			boost::regex expression("\\s+");
			std::vector< std::string > input_element_type_as_char;
                       
			boost::regex_split(std::back_inserter(input_element_type_as_char), input, expression); 

			for (std::vector<std::string>::iterator it = input_element_type_as_char.begin(), 
				end = input_element_type_as_char.end(); it != end; ++it) 
			{ 
				char c = (*it).at(0);
				switch(c)
				{
				case 'D': case 'd': encryption_task_type.push_back(DECRYPT); break;
				case 'E': case 'e': encryption_task_type.push_back(ENCRYPT); break;
					default: break;
				} 
			} 
		}
		
    }
    catch(exception& e)
    {
		cout << e.what() << std::endl;
    }  
}

bool dispatch_struct::post_url_encoded_data()
{
	return posting_url_encoded;
}



dispatch_struct_data::dispatch_struct_data(const char* _filename)
{
	input_data_filename = _filename;
	init();
}

void dispatch_struct_data::init(void)
{
	try {   

		if(input_data_filename.empty())
		{
			throw ("dispatch structure filename has to be specified: -c /path_to_dispatch_struct/filename"); 
		}

        // Declare a group of options that will be 
        // allowed both on command line and in
        // config file
#ifdef _WIN32
        po::options_description config("Configuration");
#else
	po::options_description config("Configuration", po::options_description::m_default_line_length*2);
#endif
        config.add_options()			
		(DISPATCH_STRUCT_OP[dispatch_struct_data::INPUT_ELEM_VALUE][0],  po::value< std::string >()->default_value(""),DISPATCH_STRUCT_OP[dispatch_struct_data::INPUT_ELEM_VALUE][1])
		;

        po::options_description config_file_options;
        config_file_options.add(config);
      
        po::variables_map vm;

		ifstream ifs(input_data_filename.c_str());
    	store(parse_config_file(ifs, config_file_options), vm);
    	notify(vm);  

		if(!vm[DISPATCH_STRUCT_OP[dispatch_struct_data::INPUT_ELEM_VALUE][0]].empty())
        {
                std::string input(vm[DISPATCH_STRUCT_OP[dispatch_struct_data::INPUT_ELEM_VALUE][0]].as< std::string>());
                boost::regex expression("\\s+");
     
                boost::regex_split(std::back_inserter(input_element_value), input, expression);
/*
                for (std::vector<std::string>::iterator it = input_element_value.begin(),
                        end = input_element_value.end(); it != end; ++it) {
			std::cout << *it << endl; 
                } 
*/
        }
	}
	catch(exception& e)
    {
		cout << e.what() << std::endl;
    } 
}

std::vector< std::string > const & dispatch_struct_data::get_input_element_value()
{
	return input_element_value;
}

std::string const& dispatch_struct_data::get_input_data_filename()
{
	return input_data_filename;
}


dispatch_struct_data& dispatch_struct_data::operator=(dispatch_struct_data const& _cfg)
{
	input_element_value = _cfg.input_element_value;
	input_data_filename = _cfg.input_data_filename;

	return *this;
}

dispatch_struct_data& dispatch_struct_data::operator=(std::string const& filename)
{
	input_data_filename = filename;
	init();

	return *this;
}

std::ostream& operator << (std::ostream& ost, dispatch_struct_data const& c)
{
	ost << "input element values = " << c.input_element_value << "\n";
	ost << "input data filename = " << c.input_data_filename << std::endl;
	return ost;
}

const char* dispatch_struct_data::DISPATCH_STRUCT_OP[dispatch_struct_data::DISPATCH_STRUCT_TOT_OP][2] = 
{
	{"input-element-values", "blank separated list of input element values to be assigned"}
};
